//==- CheckSizeofPointer.cpp - Check for sizeof on pointers ------*- C++ -*-==//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//  This file defines a check for unintended use of sizeof() on pointer
//  expressions.
//
//===----------------------------------------------------------------------===//

#include "ClangSACheckers.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"

//qali-add
#include "clang/helper.h"


//qali-add for statistics
namespace feature {	
	set<int> mu1_ops;
	set<const void *> mu1_funcs;
	int mu1 = 0; // number of unique operators
	int N1 = 0; // total occurences of operators

	set<const void *> mu2_ptrs;
	set<uint64_t> mu2_ints;
	int mu2_odds = 0;
	int mu2 = 0; // number of unique operands
	int N2 = 0; // totoal occurences of operands

	int mu2_prime = 0;	//potential operand count. (the number of arguments to the module)

	int branchCount = 0;
}
using namespace feature;
//===----------------------------------------------------------------------===//
// FindIdenticalExprVisitor - Identify nodes using identical expressions.
//===----------------------------------------------------------------------===//

namespace {
	class FindIdenticalExprVisitor
		: public RecursiveASTVisitor<FindIdenticalExprVisitor> {
		BugReporter &BR;
		const CheckerBase *Checker;
		AnalysisDeclContext *AC;
	public:
		explicit FindIdenticalExprVisitor(BugReporter &B,
			const CheckerBase *Checker,
			AnalysisDeclContext *A)
			: BR(B), Checker(Checker), AC(A) {}
		// FindIdenticalExprVisitor only visits nodes
		// that are binary operators, if statements or
		// conditional operators.
	//	bool VisitBinaryOperator(const BinaryOperator *B);
	//	bool VisitIfStmt(const IfStmt *I);
	//	bool VisitConditionalOperator(const ConditionalOperator *C);
		bool VisitStmt(Stmt *S);

	private:
		void reportIdenticalExpr(const BinaryOperator *B, bool CheckBitwise,
			ArrayRef<SourceRange> Sr);
		void checkBitwiseOrLogicalOp(const BinaryOperator *B, bool CheckBitwise);
		void checkComparisonOp(const BinaryOperator *B);
	};
} // end anonymous namespace

void FindIdenticalExprVisitor::reportIdenticalExpr(const BinaryOperator *B,
	bool CheckBitwise,
	ArrayRef<SourceRange> Sr) {
	StringRef Message;
	if (CheckBitwise)
		Message = "identical expressions on both sides of bitwise operator";
	else
		Message = "identical expressions on both sides of logical operator";

	PathDiagnosticLocation ELoc =
		PathDiagnosticLocation::createOperatorLoc(B, BR.getSourceManager());
	BR.EmitBasicReport(AC->getDecl(), Checker,
		"Use of identical expressions",
		categories::LogicError,
		Message, ELoc, Sr);
}

static bool FindOpsAndOprs(const Stmt *Stmt1);
bool FindIdenticalExprVisitor::VisitStmt(Stmt *S) {
	FindOpsAndOprs(S);
	return false;
}

static bool FindOpsAndOprs(const Stmt *Stmt1) {
	if (!Stmt1 ) {
		return false;
	}	

	//1. check operands first	
	const Expr *exp = dyn_cast<Expr>(Stmt1);
	if (exp) {
		// If all children of two expressions are identical, return true.
		Expr::const_child_iterator I1 = exp->child_begin();
		while (I1 != exp->child_end() ) {
			if (*I1 )
				FindOpsAndOprs(*I1);
			++I1;
		}		
	}
	
	int stmtClass = Stmt1->getStmtClass();
	switch (stmtClass) {
		default:
			return false;
		case Stmt::CallExprClass: {
			const CallExpr *callExpr = cast<CallExpr>(Stmt1);
			if (callExpr) {
				if (const FunctionDecl *fdecl = callExpr->getDirectCallee())
				{
					mu1_funcs.insert(fdecl);
					mu2_prime += fdecl->getNumParams();
				}
				++N1;
			}			
			return true;
		}
		case Stmt::ArraySubscriptExprClass:
		case Stmt::OMPArraySectionExprClass:
		case Stmt::ImplicitCastExprClass:
		case Stmt::ParenExprClass:
		case Stmt::BreakStmtClass:
		case Stmt::ContinueStmtClass:
		case Stmt::NullStmtClass:
		//	mu1_ops.insert(stmtClass);
		//	++ N1;
			return true;
		case Stmt::CStyleCastExprClass: 
			mu1_ops.insert(stmtClass);
			++ N1;
			return true;
		case Stmt::ReturnStmtClass: {
			mu1_ops.insert(Stmt::ReturnStmtClass);
			++ N1;
			const ReturnStmt *ReturnStmt1 = cast<ReturnStmt>(Stmt1);
			FindOpsAndOprs(ReturnStmt1->getRetValue());
			return true;
		}
		case Stmt::ForStmtClass: {
			++branchCount;
			const ForStmt *ForStmt1 = cast<ForStmt>(Stmt1);
			FindOpsAndOprs(ForStmt1->getInit());
			FindOpsAndOprs(ForStmt1->getCond());
			FindOpsAndOprs(ForStmt1->getInc());
			FindOpsAndOprs(ForStmt1->getBody());
			return true;
		}
		case Stmt::DoStmtClass: {
			++branchCount;
			const DoStmt *DStmt1 = cast<DoStmt>(Stmt1);
			FindOpsAndOprs(DStmt1->getCond());
			FindOpsAndOprs(DStmt1->getBody());
			return true;
		}
		case Stmt::WhileStmtClass: {
			++branchCount;
			//mu1.insert(Stmt::WhileStmtClass);
			//++ N1;
			const WhileStmt *WStmt1 = cast<WhileStmt>(Stmt1);
			FindOpsAndOprs(WStmt1->getCond());
			FindOpsAndOprs(WStmt1->getBody());
			return true;
		}
		case Stmt::IfStmtClass: {
			++branchCount;
			//mu1.insert(Stmt::IfStmtClass);
			//++ N1;
			const IfStmt *IStmt1 = cast<IfStmt>(Stmt1);
			FindOpsAndOprs(IStmt1->getCond());
			FindOpsAndOprs(IStmt1->getThen());
			FindOpsAndOprs(IStmt1->getElse());			
			return true;
		}
		case Stmt::CompoundStmtClass: {			
			const CompoundStmt *CompStmt1 = cast<CompoundStmt>(Stmt1);	
			CompoundStmt::const_body_iterator I1 = CompStmt1->body_begin();
			while (I1 != CompStmt1->body_end() ) {
				FindOpsAndOprs(*I1);
				++I1;
			}
			return true;
		}
		case Stmt::CompoundAssignOperatorClass:
		case Stmt::BinaryOperatorClass: {
			const BinaryOperator *BinOp1 = cast<BinaryOperator>(Stmt1);
			mu1_ops.insert(BinOp1->getOpcode());
			++ N1;
			return true;
		}
		//equvalent to if ==..then.=..else.=.
		case Stmt::ConditionalOperatorClass: {			
			++branchCount;
			N1 += 3;	
			return true;
		}
		case Stmt::UnaryOperatorClass: {
			const UnaryOperator *UnaryOp1 = cast<UnaryOperator>(Stmt1);
			mu1_ops.insert(UnaryOp1->getOpcode());
			++ N1;
			return true;
		}
		
		//////////////// for operands only
		case Stmt::CharacterLiteralClass: {
			const CharacterLiteral *CharLit1 = cast<CharacterLiteral>(Stmt1);
			mu2_ints.insert(CharLit1->getValue());
			++ N2;
			return true;
		}
		case Stmt::IntegerLiteralClass: {
			const IntegerLiteral *IntLit1 = cast<IntegerLiteral>(Stmt1);
			llvm::APInt I1 = IntLit1->getValue();
			mu2_ints.insert(*(I1.getRawData()));
			++ N2;
			return true;
		}
		case Stmt::FloatingLiteralClass:
		case Stmt::StringLiteralClass:		{
			++mu2_odds;
			++ N2;
			return true;
		}
		case Stmt::DeclRefExprClass: {
			const DeclRefExpr *DeclRef1 = cast<DeclRefExpr>(Stmt1);			
			mu2_ptrs.insert(DeclRef1->getDecl());
			++ N2;
			return true;
		}		
		case Stmt::MemberExprClass: {
			const MemberExpr *MemberStmt1 = cast<MemberExpr>(Stmt1);
			mu2_ptrs.insert(MemberStmt1->getMemberDecl());
			++ N2;
			return true;
		}		
	}
	return true;
}

//===----------------------------------------------------------------------===//
// SizeofPointerChecker
//===----------------------------------------------------------------------===//

namespace {
	class SizeofPointerChecker : public Checker<check::ASTCodeBody> {
	public:
		void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
			BugReporter &BR) const {
			FindIdenticalExprVisitor Visitor(BR, this, Mgr.getAnalysisDeclContext(D));
			Visitor.TraverseDecl(const_cast<Decl *>(D));
		}
	};
}

void ento::registerSizeofPointerChecker(CheckerManager &mgr) {
	mgr.registerChecker<SizeofPointerChecker>();
}